use crate::config::AppConfig;
use crate::data::{ANode, Node};
use crate::types::{NodePosition, NodeType};
use eframe::{egui::{Color32, Galley, Id, LayerId, NumExt, Order, Pos2, Response, Sense, TextStyle, Ui, Vec2, Widget, WidgetInfo, WidgetText, WidgetType}, emath::Rect};
use eframe::egui::CornerRadius;
use eframe::epaint::{CubicBezierShape, Stroke};
use std::collections::HashMap;
use std::sync::Arc;

// 控制点类型
#[derive(Eq, Hash, PartialEq)]
enum ControlPoint {
    Parent,
    Child,
    RelateLeft,
    RelateRight,
}

struct BezierInfo {
    start_pos: Pos2,
    end_pos: Pos2,
    start_control: Pos2,
    end_control: Pos2,
    fill_color: Color32,
    stroke: Stroke
}

impl BezierInfo {
    pub fn new(start_pos: Pos2, end_pos: Pos2, start_control: Pos2, end_control: Pos2) -> Self {
        BezierInfo {
            start_pos,
            end_pos,
            start_control,
            end_control,
            fill_color: Default::default(),
            stroke: Default::default(),
        }
    }
}

pub struct NodeUI {
    node: ANode,
    // 控制点
    control_points: HashMap<ControlPoint, Response>,
    node_position: NodePosition,
}

impl NodeUI {
    pub fn new(node: ANode, node_position: NodePosition) -> NodeUI {
        Self {
            node,
            control_points: Default::default(),
            node_position,
        }
    }

    // 初始化控制点
    fn draw_control_point(&mut self, rect: Rect, control_point: ControlPoint, ui: &mut Ui) {
        let mut center_pos = Pos2::ZERO;
        let mut connected = false;
        if let Ok(node) = self.node.try_lock() {
            center_pos = match control_point {
                ControlPoint::Parent => {
                    if let NodeType::Composite(is_root, _) = node.node_type.value {
                        if is_root {
                            return;
                        };
                    }
                    connected = node.parent.is_some();
                    rect.center_top()
                }
                ControlPoint::Child => {
                    if let NodeType::Leaf(_) = node.node_type.value {
                        return
                    }
                    connected = !node.children.is_empty();
                    rect.center_bottom()
                }
                ControlPoint::RelateLeft => {
                    if let NodePosition::Relate = self.node_position { return; };
                    match node.node_type.value {
                        NodeType::Composite(is_root, is_condition) => {
                            if !is_root && is_condition {
                                return;
                            }
                        }
                        NodeType::Leaf(is_condition) => {
                            if is_condition && node.parent.is_some() {
                                return;
                            };
                        }
                    }
                    connected = node.relate.is_some() || node.relate_parent.is_some();
                    rect.left_center()
                }
                ControlPoint::RelateRight => {
                    match self.node_position {
                        NodePosition::Relate => {},
                        _ => { return; },
                    };
                    connected = node.relate.is_some() || node.relate_parent.is_some();
                    rect.right_center()
                },
            };
        };

        let config = AppConfig::get(ui.ctx());
        let radius = config.control_point_radius;
        let (color, stroke) = config.get_control_point_fill_color_and_stroke(connected);
        let control_rect = Rect::from_center_size(center_pos, Vec2::splat(radius * 2.));
        let sense = match control_point {
            ControlPoint::Parent | ControlPoint::RelateRight => {
                Sense::click()
            }
            _ => {
                Sense::drag()
            }
        };
        let response = ui.allocate_rect(
            control_rect,
            sense,
        );
        // 设置拖拽的负载
        response.dnd_set_drag_payload(Arc::clone(&self.node));
        ui.painter_at(ui.ctx().screen_rect())
            .with_layer_id(LayerId::new(Order::Foreground, Id::new("control_point")))
            .circle(response.rect.center(), radius, color, stroke);
        self.control_points.insert(control_point, response);
    }

    // 绘制连接贝塞尔曲线
    fn draw_connect_bezier(&self, ui: &Ui, node_rect: Rect) {
        // [(start_pos, start_control, end_pos, end_control)]
        let mut bezier_infos: Vec<BezierInfo> = vec![];
        match self.node_position {
            NodePosition::Parent => {
                // 连接子节点
                if let Ok(node) = self.node.try_lock() {
                    self.draw_child_connect_bezier(ui, &node.children, &node_rect, &mut bezier_infos);
                };
            },
            NodePosition::Current => {
                if let Ok(node) = self.node.try_lock() {
                    let config = AppConfig::get(ui.ctx());
                    // 连接关联节点
                    if let Some(relate) = &node.relate {
                        if let Ok(relate) = relate.try_lock() {
                            let mut bezier_info = Self::get_bezier_info(&ControlPoint::RelateLeft, &node_rect, relate.rect.right_center());
                            (bezier_info.fill_color, bezier_info.stroke) = config.get_bezier_fill_color_and_stroke(
                                self.node_position,
                                NodePosition::Relate,
                                true
                            );
                            bezier_infos.push(bezier_info);
                        }
                    };

                    // 连接条件节点的关联节点
                    if let Some(relate) = &node.relate_parent {
                        if let Some(relate) = relate.upgrade() {
                            if let Ok(relate) = relate.try_lock() {
                                let mut bezier_info = Self::get_bezier_info(&ControlPoint::RelateLeft, &node_rect, relate.rect.right_center());
                                (bezier_info.fill_color, bezier_info.stroke) = config.get_bezier_fill_color_and_stroke(
                                    self.node_position,
                                    NodePosition::Relate,
                                    true
                                );
                                bezier_infos.push(bezier_info);
                            }
                        }
                    };
                    // 连接子节点
                    self.draw_child_connect_bezier(ui, &node.children, &node_rect, &mut bezier_infos);
                };
            },
            _ => {},
        }
        for bezier_info in bezier_infos {
            Self::draw_bezier(ui, bezier_info);
        }
    }

    fn draw_child_connect_bezier(&self, ui: &Ui, children: &Vec<ANode>, node_rect: &Rect, vector: &mut Vec<BezierInfo>) {
        let config = AppConfig::get(ui.ctx());
        let end_node_position = match self.node_position {
            NodePosition::Current => Some(NodePosition::Child),
            NodePosition::Parent => None,
            _ => { return; }
        };
        for child in children {
            if let Ok(child) = child.try_lock() {
                let mut bezier_info = Self::get_bezier_info(&ControlPoint::Child, node_rect, child.rect.center_top());
                let end_node_position = match end_node_position {
                    Some(end_node_position) => { end_node_position }
                    None => {
                        let curr_node = Node::get_current_node(ui.ctx());
                        if curr_node.try_lock().is_ok() {
                            NodePosition::Child
                        } else {
                            NodePosition::Current
                        }
                    }
                };
                (bezier_info.fill_color, bezier_info.stroke) = config.get_bezier_fill_color_and_stroke(
                    self.node_position,
                    end_node_position,
                    true);
                vector.push(bezier_info);
            };
        };
    }

    // 绘制拖拽贝塞尔曲线
    fn draw_drag_bezier(&self, ui: &Ui) {
        for (control_point, response) in self.control_points.iter() {
            if response.dragged() {
                let end_pos = ui.input(|i| i.pointer.interact_pos()).unwrap_or(Pos2::ZERO);
                let mut bezier_info = Self::get_bezier_info(control_point, &response.rect, end_pos);
                let config = AppConfig::get(ui.ctx());
                (bezier_info.fill_color, bezier_info.stroke) = config.get_bezier_fill_color_and_stroke(
                    self.node_position,
                    self.node_position,
                    false
                );
                Self::draw_bezier(ui, bezier_info);
            }
            // 按下快捷键同时拖拽结束
            if self.is_create_empty_node_key_press(ui) && response.drag_stopped() {
                // 空放创建新节点
                self.create_new_node(control_point);
            }
        }
    }

    fn create_new_node(&self, control_point: &ControlPoint) {
        if let Ok(mut node) = self.node.try_lock() {
            let mut new_node = Node::new();
            match control_point {
                ControlPoint::Child => {
                    new_node.parent = Some(Arc::downgrade(&self.node));
                    new_node.node_type = match node.node_type.value {
                        NodeType::Leaf(is_condition) => {
                            NodeType::Composite(false, is_condition).into()
                        }
                        NodeType::Composite(_, is_condition) => {
                            NodeType::Composite(false, is_condition).into()
                        }
                    };
                    node.children.push(new_node.into());
                }
                ControlPoint::RelateLeft => {
                    new_node.relate_parent = Some(Arc::downgrade(&self.node));
                    new_node.node_type = NodeType::Composite(true, true).into();
                    node.relate = Some(new_node.into());
                }
                _ => {}
            }
        }
    }

    // 创建新节点触发键
    fn is_create_empty_node_key_press(&self, ui: &Ui) -> bool {
        ui.input(|i| i.modifiers.shift)
    }

    fn draw_bezier(ui: &Ui, bezier_info: BezierInfo) {
        let bezier = CubicBezierShape::from_points_stroke(
            [bezier_info.start_pos, bezier_info.start_control, bezier_info.end_control, bezier_info.end_pos],
            false,
            bezier_info.fill_color,
            bezier_info.stroke,
        );
        ui.painter_at(ui.ctx().screen_rect())
            .with_layer_id(LayerId::new(Order::Background, Id::new("bezier")))
            .add(bezier);
    }

    fn get_bezier_info(control_point: &ControlPoint, rect: &Rect, end_pos: Pos2) -> BezierInfo {
        let (start_pos, start_control, end_control) = match control_point {
            ControlPoint::Parent | ControlPoint::Child => {
                let start_pos = if *control_point == ControlPoint::Parent { rect.center_top() } else { rect.center_bottom() };
                let y_scale = (start_pos.y - end_pos.y) / 0.8;
                let sign = if y_scale > 0.0 { 1.0f32 } else { -1.0f32 };
                let start_control = start_pos - Vec2::Y * y_scale;
                let end_control = end_pos + sign * Vec2::Y * 50.0;
                (start_pos, start_control, end_control)
            }
            ControlPoint::RelateLeft | ControlPoint::RelateRight => {
                let start_pos = if *control_point == ControlPoint::RelateLeft { rect.left_center() } else { rect.right_center() };
                let scale = (start_pos.x - end_pos.x) / 1.0;
                let start_control = start_pos - Vec2::X * scale;
                let end_control = end_pos + Vec2::X * scale;
                (start_pos, start_control, end_control)
            }
        };
        BezierInfo::new(start_pos, end_pos, start_control, end_control)
    }

    fn draw_node(&mut self, ui: &mut Ui, rect: Rect, response: &Response, galley: Arc<Galley>) {
        let config = AppConfig::get(ui.ctx());
        let painter = ui.painter_at(ui.ctx().screen_rect()).with_layer_id(LayerId::new(Order::Middle, Id::new("node")));
        painter.rect(rect, CornerRadius::ZERO, config.node_bg_fill_color, config.node_stroke, eframe::egui::StrokeKind::Middle);

        // 绘制控制点
        let control_points = vec![
            ControlPoint::Parent,
            ControlPoint::Child,
            ControlPoint::RelateLeft,
            ControlPoint::RelateRight,
        ];
        for control_point in control_points {
            self.draw_control_point(rect, control_point, ui);
        };

        // 绘制节点内容
        let button_padding = config.node_padding;
        let text_pos = ui
            .layout()
            .align_size_within_rect(galley.size(), rect.shrink2(button_padding))
            .min;
        let visuals = ui.style().interact(response);
        painter.galley(text_pos, galley, visuals.text_color());

        // 创建新节点中
        if self.is_create_empty_node_key_press(ui) { return; }

        // 控制节点拖拽释放
        if let Some(node) = response.dnd_release_payload::<ANode>() {
            if let Ok(node) = node.try_lock() {
                if let Ok(curr_node) = self.node.try_lock() {
                    // 其他节点
                    println!("DND Release Payload {}, {}", curr_node.id, node.id);
                } else {
                    // 自身节点
                    println!("DND Release Payload {}", node.id);
                };
            };
        };
    }
}

impl Widget for NodeUI {
    fn ui(mut self, ui: &mut Ui) -> Response {
        let config = AppConfig::get(ui.ctx());
        let button_padding = config.node_padding;
        let text_wrap_width = ui.available_width() - 2.0 * button_padding.x;
        let text = match self.node.try_lock() {
            Ok(node) => { node.title.value.clone() }
            Err(_) => { String::from("") }
        };
        let galley = WidgetText::from(text).into_galley(ui, None, text_wrap_width, TextStyle::Button);
        let mut desired_size = Vec2::ZERO;
        desired_size.x += galley.size().x;
        desired_size.y = desired_size.y.max(galley.size().y);
        desired_size += 2.0 * button_padding;
        let _ = desired_size.at_least(config.node_size);

        let (rect, response) = ui.allocate_at_least(desired_size, Sense::click());
        response.widget_info(|| {
            WidgetInfo::labeled(WidgetType::Button, ui.is_enabled(), galley.text())
        });

        self.draw_node(ui, rect, &response, galley);
        self.draw_connect_bezier(ui, response.rect);
        self.draw_drag_bezier(ui);

        if let Some(cursor) = ui.visuals().interact_cursor {
            if response.hovered() {
                ui.ctx().set_cursor_icon(cursor);
            };
        };
        if let Ok(mut node) = self.node.try_lock() {
            node.rect = rect;
        };
        response
    }
}
